﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace Job.Core.Common
{
    /// <summary>
    /// 对象工厂，可通过 objectFactory 配置节设置要生成的目标对象。
    /// </summary>
    public static class ObjectFactory
    {
        #region Init

        private const string sectionName = "objectFactory";

        private static readonly object singletonSyncObj = new object();

        /// <summary>
        /// 缓存类型对象工厂设置。
        /// </summary>
        private static Dictionary<Type, TypeAndParams> typesDic = InitTypes();

        /// <summary>
        /// 缓存命名对象工厂设置。
        /// </summary>
        private static Dictionary<string, TypeAndParams> namedsDic = InitNameds();

        /// <summary>
        /// 获取初始化类型对象工厂设置。
        /// </summary>
        /// <returns>设置字典。</returns>
        private static Dictionary<Type, TypeAndParams> InitTypes()
        {
            Dictionary<Type, TypeAndParams> result = new Dictionary<Type, TypeAndParams>();

            ObjectFactoryConfigSection config = ConfigurationManager.GetSection(sectionName) as ObjectFactoryConfigSection;
            if (config != null)
            {
                foreach (object type in config.Types)
                {
                    var typeSetting = (ObjectFactoryTypeSettings)type;
                    var tap = new TypeAndParams { Type = Type.GetType(typeSetting.Target), Singleton = typeSetting.Singleton };
                    if (typeSetting.Params.Count > 0)
                    {
                        tap.Params = new object[typeSetting.Params.Count];
                        int i = 0;
                        foreach (object param in typeSetting.Params)
                        {
                            var paramSetting = (ObjectFactoryParam)param;
                            if (!string.IsNullOrEmpty(paramSetting.Value))
                            {
                                tap.Params[i++] = (!string.IsNullOrEmpty(paramSetting.Type)) ? Convert.ChangeType(paramSetting.Value, Type.GetType(paramSetting.Type)) : paramSetting.Value;
                                continue;
                            }
                        }
                    }

                    result.Add(Type.GetType(typeSetting.Src), tap);
                }
            }

            return result;
        }

        /// <summary>
        /// 获取初始化命名类型对象工厂设置。
        /// </summary>
        /// <returns>设置字典。</returns>
        private static Dictionary<string, TypeAndParams> InitNameds()
        {
            Dictionary<string, TypeAndParams> result = new Dictionary<string, TypeAndParams>();

            ObjectFactoryConfigSection config = ConfigurationManager.GetSection(sectionName) as ObjectFactoryConfigSection;
            if (config != null)
            {
                foreach (object named in config.Nameds)
                {
                    var namedSetting = (ObjectFactoryNamedSettings)named;
                    var tap = new TypeAndParams { Type = Type.GetType(namedSetting.Type), Singleton = namedSetting.Singleton };
                    if (namedSetting.Params.Count > 0)
                    {
                        tap.Params = new object[namedSetting.Params.Count];
                        int i = 0;
                        foreach (object param in namedSetting.Params)
                        {
                            var paramSetting = (ObjectFactoryParam)param;
                            if (!string.IsNullOrEmpty(paramSetting.Value))
                            {
                                tap.Params[i++] = (!string.IsNullOrEmpty(paramSetting.Type)) ? Convert.ChangeType(paramSetting.Value, Type.GetType(paramSetting.Type)) : paramSetting.Value;
                                continue;
                            }                            
                        }
                    }

                    result.Add(namedSetting.Name, tap);
                }
            }

            return result;
        }        

        #endregion

        #region Create

        /// <summary>
        /// 创建已配置指定类型的实例。
        /// </summary>
        /// <typeparam name="T">类型。</typeparam>
        /// <returns>指定类型的实例。</returns>        
        public static T Create<T>()
        {
            return Create<T>(typeof(T));
        }

        /// <summary>
        /// 创建已配置指定类型的实例。
        /// </summary>
        /// <typeparam name="T">类型。</typeparam>
        /// <param name="type">类型。 </param>
        /// <returns>指定类型的实例。</returns>
        public static T Create<T>(Type type)
        {
            if (type == null)
                throw new ArgumentNullException("type");

            if (typesDic.ContainsKey(type))
            {
                TypeAndParams tap = typesDic[type];
                if (tap.Singleton)
                {
                    if (tap.SingletonValue == null)
                    {
                        lock (singletonSyncObj)
                        {
                            if (tap.SingletonValue == null)
                                tap.SingletonValue = Activator.CreateInstance(tap.Type, tap.Params);
                        }
                    }
                    return (T)tap.SingletonValue;
                }

                return (T)Activator.CreateInstance(tap.Type, tap.Params);
            }
            else
                return Activator.CreateInstance<T>();
        }

        /// <summary>
        /// 创建已配置命名的实例。
        /// </summary>
        /// <typeparam name="T">类型。</typeparam>
        /// <param name="name">命名。</param>
        /// <returns>指定命名的实例。</returns>
        public static T Create<T>(string name)
        {
            if (string.IsNullOrEmpty(name))
                throw new ArgumentNullException("name");

            if (namedsDic.ContainsKey(name))
            {
                TypeAndParams tap = namedsDic[name];
                if (tap.Singleton)
                {
                    if (tap.SingletonValue == null)
                    {
                        lock (singletonSyncObj)
                        {
                            if (tap.SingletonValue == null)
                                tap.SingletonValue = Activator.CreateInstance(tap.Type, tap.Params);
                        }
                    }
                    return (T)tap.SingletonValue;
                }

                return (T)Activator.CreateInstance(tap.Type, tap.Params);
            }

            return Activator.CreateInstance<T>();
        }

        /// <summary>
        /// 保存类型与参数及相关设置的类。
        /// </summary>
        private class TypeAndParams
        {
            /// <summary>
            /// 类型。
            /// </summary>
            public Type Type { get; set; }

            /// <summary>
            /// 参数。
            /// </summary>
            public object[] Params { get; set; }

            /// <summary>
            /// 是否为单例。
            /// </summary>
            public bool Singleton { get; set; }

            /// <summary>
            /// 用于保存单例实例。
            /// </summary>
            public object SingletonValue { get; set; }
        }

        #endregion

        #region Clone

        /// <summary>
        /// 深克隆一个对象。
        /// </summary>
        /// <param name="obj">要克隆的对象。</param>
        /// <returns>克隆出的对象。</returns>
        public static object Clone(object obj)
        {
            if (obj == null) return obj;

            using (MemoryStream ms = new MemoryStream())
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(ms, obj);
                ms.Position = 0;
                return formatter.Deserialize(ms);
            }
        }

        /// <summary>
        /// 深克隆一个对象。
        /// </summary>
        /// <param name="obj">要克隆的对象。</param>
        /// <returns>克隆出的对象。</returns>
        public static T Clone<T>(T obj)
        {
            if (obj == null) return obj;

            using (MemoryStream ms = new MemoryStream())
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(ms, obj);
                ms.Position = 0;
                return (T)formatter.Deserialize(ms);
            }
        }

        #endregion
    }

}